from main.model.logic.interruption import Interruption
from threading import Thread, Condition
from main.commons.func_aux import find_in_seq
from main.exceptions.exception import NoPCBFinishedInIOException

class Turn(object):
    def __init__(self, ticks_left):
        self.__ticks_left = ticks_left
        self.__pcbs = []
    
    def tick(self):
        self.__ticks_left -= 1
        
    def add(self, pcb):
        self.__pcbs.append(pcb)
        
    @property
    def ticks_left(self):
        return self.__ticks_left
    
    @property
    def pcbs(self):
        return self.__pcbs
    
class InOut(Thread):
    
    def __init__(self):
        Thread.__init__(self)
        self.__turns       = []
        self.__kernel     = None
        self.__stop       = False
        self.__waiting  = Condition()
        
    def run(self):
        while(not self.__stop):
            with self.__waiting:
                self.__waiting.wait()
            for turn in self.__turns:
                turn.tick()
            try:
                turn0 = self.get_turn(0)
                self.send_to_ready(turn0.pcbs)
                self.__turns.remove(turn0)
            except NoPCBFinishedInIOException:
                pass
            
    def stop(self):
        self.__stop = True
    
    def get_turn(self, index):
        turn = find_in_seq(self.__turns, lambda x : x.ticks_left == index)
        if not turn:
            raise NoPCBFinishedInIOException
        return turn
     
    def send_to_ready(self, pcbs):
        for pcb in pcbs:
            self.kernel.add_interruption(Interruption(Interruption.IO_FINISH, pcb)) 
        
    def add(self, pcb, ticks):
        try:
            turn = self.get_turn(ticks)
        except NoPCBFinishedInIOException:
            turn = Turn(ticks)
            self.__turns.append(turn)
        turn.add(pcb)
        
    def tick(self, time_in_seconds):
        with self.__waiting:
            self.__waiting.notify()
                
    @property   
    def kernel(self):
        return self.__kernel
    
    @kernel.setter
    def kernel(self, kernel):
        self.__kernel = kernel
    
                